package xfuzzy.xfhl.view;

import java.awt.*;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import java.awt.event.*;
import java.io.File;

import xfuzzy.Xfuzzy;
import xfuzzy.XmlParser;
import xfuzzy.lang.Specification;
import xfuzzy.util.XConstants;
import xfuzzy.util.XDialog;
import xfuzzy.util.XFileChooser;
import xfuzzy.util.XFileFilter;
import xfuzzy.util.XMenu;
import xfuzzy.xfhl.controller.XfhlController;
import xfuzzy.xfhl.model.XfhlConfig;
import xfuzzy.xfhl.model.XfhlProcess;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JMenuBar;

/**
 * La clase principal de la vista de la herramienta Xfhl
 * 
 * @author Sergio Cala Cordero, Septiembre 2009, Herramienta XFHL
 * 
 */

public class XfhlView extends JFrame implements Observer {

	private static final long serialVersionUID = 1L;

	/**
	 * Panel que contiene toda el JFrame
	 */
	private JPanel jContentPane = null;

	/**
	 * Panel donde se muestra la mejor estructura
	 */
	private JPanel jPnlBest = null;

	/**
	 * Panel que contiene el JTabbedPane con todas las estructuras que se estan
	 * evaluando
	 */
	private JPanel jPnlUnder = null;

	/**
	 * Contiene tantas ventanas como hebras tenemos ejecutando nuestro codigo
	 */
	private JTabbedPane jTabbedPane = null;

	/**
	 * Barra de progreso del sistema total
	 */
	private JProgressBar jPBarGeneral = null;

	/**
	 * Configuracion de la aplicacion XfhlConfig
	 */
	private XfhlConfig xfhlConfig; // @jve:decl-index=0:

	/**
	 * Instancia al proceso de la herramienta Xfhl
	 */
	private XfhlProcess xfhlProcess;

	/**
	 * Array de XfhlDataView que son los paneles que contiene el jtabbedpane
	 */
	private XfhlDataView[] currentDataView;

	/**
	 * Instancia al mejor XfhlDataView
	 */
	private XfhlDataView bestDataView;

	/**
	 * Atributo que nos dice la hebra activa para mostrar su proceso
	 */
	private int threadActivate;

	/**
	 * JPanel de la hebra activa
	 */
	private JPanel jPnlProcBarCurrentThread = null;

	/**
	 * Instancia al controlador
	 */
	private XfhlController xfhlControllerView;

	/**
	 * Menu file
	 */
	private XMenu[] menu;

	/**
	 * Ventana principal de xfuzzy
	 */
	private Xfuzzy xfuzzy;

	/**
	 * El sistema difuso donde guardar el resultado
	 */
	private Specification original;

	/**
	 * Boton ejecutar
	 */
	private JButton jBtnRun = null;

	/**
	 * Boton cerrar
	 */
	private JButton jBtnClose = null;

	/**
	 * Boton recargar
	 */
	private JButton jBtnReload = null;

	/**
	 * Boton aplicar
	 */
	private JButton jBtnApply = null;

	/**
	 * Boton guardar
	 */
	private JButton jBtnSave = null;

	/**
	 * Tiempo inicial
	 */
	private long timeIni;

	/**
	 * This is the default constructor
	 */
	public XfhlView(XfhlController xfhlControllerView, Xfuzzy xfuzzy, Specification original) {
		super();
		this.xfhlControllerView = xfhlControllerView;
		this.xfuzzy = xfuzzy;
		this.original = original;
		initialize();
		this.setVisible(true);  // esta linea puede sobrar
	}

	/**
	 * Metodo que inicia el proceso
	 * 
	 * @param xfhlProcess
	 *            Instancia al xhlProcess
	 * @param currentDataView
	 *            Array de objetos XfhlDataView para ver los graficos
	 */
	public void run(XfhlProcess xfhlProcess, XfhlDataView[] currentDataView) {
		
		// Iniciamos el contador
		this.timeIni = System.currentTimeMillis();

		enabledButton(1, true); // Reload
		enabledButton(2, true); // Apply
		enabledButton(3, true); // Save
		enabledButton(4, false); // Close

		this.jContentPane.revalidate();

		this.menu[0].setEnabled(false); // desabilitamos el menu entero

		this.xfhlProcess = xfhlProcess;
		this.xfhlConfig = this.xfhlProcess.getConfig();
		this.currentDataView = currentDataView;

		initialize2();

		for (int i = 0; i < this.xfhlConfig.getNumThread(); i++) {

			// Añadimos las pestañas del jtabbedPane que son el jpanel del
			// DataView
			this.jTabbedPane.addTab("Thread " + (i + 1), this.currentDataView[i].getJPanel());
		}

		// El thread activo es la 0 y le asignamos
		this.setThreadActivate(0);
		this.jPnlProcBarCurrentThread.add(this.currentDataView[0].getJProgressBar());
		this.xfhlProcess.run();

		this.jBtnRun.setText("Pause");
		this.jBtnRun.setActionCommand("Pause");

	}

	/**
	 * Hablilita un boton del menu
	 * 
	 * @param ind
	 *            indice del boton a habilitar 0 RUN/PAUSE/CONTINUE 1 RELOAD 2
	 *            APPLY 3 SAVE 4 CLOSE
	 */
	private void enabledButton(int ind, boolean value) {
		switch (ind) {
			case 0:
				this.jBtnRun.setEnabled(value);
				break;
			case 1:
				this.jBtnReload.setEnabled(value);
				break;
			case 2:
				this.jBtnApply.setEnabled(value);
				break;
			case 3:
				this.jBtnSave.setEnabled(value);
				break;
			case 4:

				this.jBtnClose.setEnabled(value);
				break;
			default:
				break;
		}
	}

	/**
	 * Hablilita o deshabilita un boton del menu
	 * 
	 * @param men
	 *            0 FILE
	 * @param ind
	 *            Opcion del menu 
	 *            	0 New Configuration 
	 *            	1 Edit Configuration 
	 *            	2 Load Configuration 
	 *            	3 Save Configuration
	 */
	private void enabledOptionMenu(int men, int ind, boolean value) {
		this.menu[men].getItem(ind).setEnabled(value);
	}

	/**
	 * Metodo que se crea el jframe y el menu
	 * 
	 * @return void
	 */
	private void initialize() {
		
		this.setSize(900, 731);
		this.setJMenuBar(getJJMenuBar());
		this.setLocationRelativeTo(null);
		this.setResizable(false);
		this.setTitle("Xfhl");
		this.setFont(XConstants.font);

		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent evt) {
				dispose();
			}
		});
		initialize2();
	}

	/**
	 * Metodo que se crea el resto de elementos de la interfaz, cuando ya
	 * sabemos que xfhlProcess vamos a ejecutar
	 */
	private void initialize2() {
		this.setContentPane(getJContentPane());
	}

	/**
	 * This method initializes jContentPane
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getJContentPane() {
		
		jContentPane = new JPanel();
		jContentPane.setLayout(null);
		jContentPane.setFont(XConstants.font);
		jContentPane.setSize(new Dimension(872, 663));
		jContentPane.add(getJPnlBest(), null);
		jContentPane.add(getJPnlUnder(), null);
		jContentPane.add(getJPBarGeneral(), null);
		jContentPane.add(getJBtnRun(), null);
		jContentPane.add(getJBtnClose(), null);
		jContentPane.add(getJBtnReload(), null);
		jContentPane.add(getJBtnApply(), null);
		jContentPane.add(getJBtnSave(), null);
		return jContentPane;
	}

	/**
	 * Guarda la mejor epecificacion hasta el momento
	 */

	public void saveSpecification() {
		
		Specification spec = this.xfhlProcess.getBestData().getSpec();

		// Copiamos la mejor especificacion hasta el momento
		if (spec != null) {
			// Elegimos el nombre del fichero de datos
			XFileChooser fileDataSet = new XFileChooser(new File(""), XFileChooser.SAVE);
			fileDataSet.addFileFilter(new XFileFilter(".xfl", "System Specification File"));
			fileDataSet.showDialog();

			if (fileDataSet.getSelectedFile() != null) {
				String ruta = fileDataSet.getSelectedFile().getPath();
				if (!ruta.endsWith(".xfl"))
					ruta += ".xfl";
				File file = new File(ruta);
				// String []
				// message={"The specification has been saved and load:", ruta};

				if (spec.save_as(file)) {
					this.xfuzzy.load(file);
					// XDialog.showMessage(null, message);
				}
				// else
				// XDialog.showMessage(null,
				// "The specification has NOT been saved.");
			}
		} else {
			XDialog.showMessage(null, "Sorry, not exist any specification.");
		}
	}

	/**
	 * Metodo que actualiza la especificicacion actual por la seleccionada en la
	 * ventana principal de xfuzzy
	 */
	public void apply() {
		
		Specification spec = this.xfhlProcess.getBestData().getSpec();

		// Copiamos la mejor especificacion hasta el momento
		if (spec != null) {
			this.original.setOperatorsets(spec.getOperatorsets());
			this.original.setTypes(spec.getTypes());
			this.original.setRulebases(spec.getRulebases());
			this.original.setSystemModule(spec.getSystemModule());
			this.original.setModified(true);
		} else {
			XDialog.showMessage(null, "Sorry, not exist any specification.");
		}
	}

	/**
	 * Guarda la configuracion de la herramienta
	 */
	public void saveConfiguration() {
		
		// Copiamos la mejor especificacion hasta el momento
		if (this.xfhlConfig != null) {
			// Elegimos el nombre del fichero de datos
			XFileChooser fileDataSet = new XFileChooser(new File(""), XFileChooser.SAVE);
			fileDataSet.addFileFilter(new XFileFilter(".xml", "System Configuration File"));
			fileDataSet.showDialog();

			if (fileDataSet.getSelectedFile() != null) {

				String ruta = fileDataSet.getSelectedFile().getPath();
				if (!ruta.endsWith(".xml"))
					ruta += ".xml";
				File file = new File(ruta);

				String[] message = { "The configuration has been saved:", ruta };

				if (this.xfhlConfig.save(file))
					XDialog.showMessage(null, message);
				else
					XDialog.showMessage(null, "The configuration has NOT been saved.");
			}
		} 
		else {
			XDialog.showMessage(null, "Sorry, not exist any configuration.");
		}
	}

	/**
	 * This method initializes jPnlBest
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getJPnlBest() {
		jPnlBest = new JPanel(new BorderLayout());
		jPnlBest.setBounds(new Rectangle(30, 15, 840, 251));
		jPnlBest.setBorder(BorderFactory.createTitledBorder(null, "Best found", TitledBorder.CENTER,
				TitledBorder.DEFAULT_POSITION, XConstants.font, new Color(51, 51, 51)));
		return jPnlBest;
	}

	public void loadConfiguration() {
		
		boolean exist = false;
		String ruta = "";
		do {
			XFileChooser fileDataSet = new XFileChooser(new File(""), XFileChooser.SELECT);
			fileDataSet.addFileFilter(new XFileFilter(".xml", "XML"));

			int sal = fileDataSet.showDialog();

			if (sal == XFileChooser.CANCELED) {
				exist = true;
			} 
			else if (fileDataSet.getSelectedFile() != null) {
				File file = new File(fileDataSet.getSelectedFile().getPath());

				if (file.exists() && file.isFile()) {
					exist = true;
					ruta = fileDataSet.getSelectedFile().getPath();
				} 
				else {
					XDialog.showMessage(null, "The xml file must exist.");
				}
			}
		} while (!exist);

		if (ruta.compareTo("") != 0) {
			XmlParser parse = new XmlParser();
			parse.parseXML(new File(ruta));
			XfhlConfig config = parse.getXfhlConfig();
			if (config != null) {
				this.setConfig(config);
			} else {
				XDialog.showMessage(null, "ERROR!! The xml configuration is incorrect.");
			}

		}
	}

	/**
	 * This method initializes jPnlUnder
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getJPnlUnder() {
		jPnlUnder = new JPanel();
		jPnlUnder.setLayout(null);
		jPnlUnder.setBounds(new Rectangle(31, 301, 841, 318));
		jPnlUnder.setBorder(BorderFactory.createTitledBorder(null, "Under Study", TitledBorder.CENTER,
				TitledBorder.DEFAULT_POSITION, XConstants.font, new Color(51, 51, 51)));
		if (this.xfhlConfig != null) {
			jPnlUnder.add(getJTabbedPane(), null);
			jPnlUnder.add(getjPnlProcBarCurrentThread(), null);
		}
		return jPnlUnder;
	}

	/**
	 * This method initializes jPBarGeneral
	 * 
	 * @return javax.swing.JProgressBar
	 */
	private JProgressBar getJPBarGeneral() {

		jPBarGeneral = new JProgressBar();
		jPBarGeneral.setBounds(new Rectangle(31, 272, 839, 20));
		if (this.xfhlConfig != null)
			jPBarGeneral.setMaximum(this.xfhlConfig.getNumStruc());
		jPBarGeneral.setValue(0);
		jPBarGeneral.setFont(XConstants.proggresfont);
		jPBarGeneral.setBackground(XConstants.proggresColorBackGround);
		String aux = "";
		if (this.xfhlConfig != null)
			aux = "Search Progress: " + this.jPBarGeneral.getValue() + " of " + this.xfhlConfig.getNumStruc()
					+ "   =>   (" + (int) (this.jPBarGeneral.getPercentComplete() * 100) + "%).";
		else
			aux = "Search Progress. Ready to RUN!";
		this.jPBarGeneral.setString(aux);
		this.jPBarGeneral.setStringPainted(true);

		return jPBarGeneral;
	}

	/**
	 * This method initializes jTabbedPane
	 * 
	 * @return javax.swing.JTabbedPane
	 */
	private JTabbedPane getJTabbedPane() {
		jTabbedPane = new JTabbedPane();
		jTabbedPane.setBounds(new Rectangle(9, 16, 826, 275));
		jTabbedPane.setFont(XConstants.font);
		// Añadimos un evento para cuando cambie de ventana
		jTabbedPane.addChangeListener(new javax.swing.event.ChangeListener() {
			public void stateChanged(javax.swing.event.ChangeEvent e) {
				int ind = jTabbedPane.getSelectedIndex();
				setThreadActivate(ind);
				jPnlProcBarCurrentThread.removeAll();
				jPnlProcBarCurrentThread.add(currentDataView[ind].getJProgressBar());
				jPnlProcBarCurrentThread.repaint();
			}
		});
		return jTabbedPane;
	}

	/**
	 * Metodo update Sincronizado para actualizar la mejor estructura
	 * 
	 * @param obs
	 */
	private synchronized void updateSin(Observable obs) {
		if (obs instanceof XfhlProcess) {
			// Comprobamos el cambio
			int change = ((XfhlProcess) obs).getChange();
			switch (change) {

				case XfhlProcess.UPDATE_BEST:
					updateBestSpec();
				case XfhlProcess.FINAL_SPEC:
					// Actualizamos la barra de progreso
					this.jPBarGeneral.setValue(this.jPBarGeneral.getValue() + 1);
					String aux;
					if (this.jPBarGeneral.getValue() == this.xfhlConfig.getNumStruc()) {
						finished();
						aux = "Search Progress: " + this.jPBarGeneral.getValue() + " of "
								+ this.xfhlConfig.getNumStruc() + "   =>   ("
								+ (int) (this.jPBarGeneral.getPercentComplete() * 100)
								+ "%) => FINISHED => Total time: " + this.totalTime();

					} else {
						aux = "Search Progress: " + this.jPBarGeneral.getValue() + " of "
								+ this.xfhlConfig.getNumStruc() + "   =>   ("
								+ (int) (this.jPBarGeneral.getPercentComplete() * 100) + "%)  => Time Stimated: "
								+ this.xfhlProcess.stimateTime();
					}
					// Actualizamos el texto de la barra de progreso
					this.jPBarGeneral.setString(aux);
					break;
				default:
					break;
			}
		}
	}

	/**
	 * Para que cada hebra nos avise cuando ha terminado y el proceso cuando se
	 * ha actualizado el mejor
	 */
	public void update(Observable arg0, Object arg1) {
		updateSin(arg0);
	}

	/**
	 * This method initializes jPnlProcBarGobal
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getjPnlProcBarCurrentThread() {
		if (jPnlProcBarCurrentThread == null) {
			jPnlProcBarCurrentThread = new JPanel(new BorderLayout());
			jPnlProcBarCurrentThread.setBounds(new Rectangle(9, 295, 824, 20));
		}
		return jPnlProcBarCurrentThread;
	}

	/**
	 * This method initializes jJMenuBar
	 * 
	 * @return javax.swing.JMenuBar
	 */
	private JMenuBar getJJMenuBar() {

		String title[] = { "Configuration" };
		String label[][] = { { "New           ", "Edit          ", "Load          ", "Save          " } };
		String command[][] = { { "NewConfiguration", "EditConfiguration", "LoadConfiguration", "SaveConfiguration" }, };

		menu = new XMenu[1];
		for (int i = 0; i < menu.length; i++)
			menu[i] = new XMenu(title[i], label[i], command[i], this.xfhlControllerView);

		// Deshabilitamos las opciones que no estan activas al principio
		// 1 -> Edit
		// 3 -> Save

		menu[0].getItem(1).setEnabled(false);
		menu[0].getItem(3).setEnabled(false);

		JMenuBar menubar = new JMenuBar();
		menubar.add(Box.createHorizontalStrut(5));
		menubar.add(menu[0]);
		menubar.add(Box.createHorizontalStrut(5));
		return menubar;
	}

	/**
	 * Lanza una ventana de nueva configuraci�n
	 */
	public void newConfiguration() {
		XfhlConfigView config = new XfhlConfigView(this);
		config.setVisible(true);
		this.setEnabled(false);
	}

	/**
	 * Lanza una ventana de configuracion para editar la configuracion existente
	 */
	public void editConfiguration() {
		XfhlConfigView config = new XfhlConfigView(this, this.xfhlConfig);
		config.setVisible(true);
		this.setEnabled(false);
	}

	/**
	 * Metodo actualiza la mejor estructura
	 * 
	 */
	private synchronized void updateBestSpec() {
		// Pintamos el mejor
		this.bestDataView = new XfhlDataView(this.xfhlProcess.getBestData().getSpec());

		this.jPnlBest.removeAll();
		this.jPnlBest.add(this.bestDataView.getBox());
		String title = "Best found (RMSE:" + this.xfhlProcess.getBestData().getFinalError() + ")";
		((TitledBorder) (this.jPnlBest.getBorder())).setTitle(title);
		// System.out.println(title);
		this.jPnlBest.repaint();
		this.jPnlBest.revalidate();
	}

	/**
	 * Obtiene la configuracion actual
	 * 
	 * @return
	 */
	public XfhlConfig getConfig() {
		return this.xfhlConfig;
	}

	/**
	 * Establece la configuracion en el jFrame y habilita y deshabilita los
	 * botones
	 * 
	 * @param xfhlConfig
	 */
	public void setConfig(XfhlConfig xfhlConfig) {
		this.xfhlConfig = xfhlConfig;

		// Generamos los paneles
		this.initialize2();
		this.jContentPane.revalidate();

		// Comprobamos si tenemos que poner los botones activos
		if (xfhlConfig != null) {
			enabledButton(0, true); // Run
			enabledOptionMenu(0, 1, true); // Edit
			enabledOptionMenu(0, 3, true); // Save

		} else {
			enabledButton(0, true); // Run
			enabledOptionMenu(0, 1, false); // Edit
			enabledOptionMenu(0, 3, false); // Save
		}
	}

	/**
	 * Cuando se llega al 100%
	 */
	private void finished() {
		this.jBtnRun.setText("Run");
		this.jBtnRun.setActionCommand("Run");

		// Habilitamos botones y opciones del menu
		enabledButton(1, false); // Reload
		enabledButton(4, true); // Close
		this.menu[0].setEnabled(true); // Habilitamos el menu
	}

	/**
	 * M�todo que para todo el proceso, no podr� ser reiniciado
	 */
	public void reload() {
		xfhlProcess.stop();
		// Actualizamos el texto de la barra de progreso
		String aux = "!STOPPED! The process has been canceled.";
		this.jPBarGeneral.setString(aux);
		this.jPBarGeneral.setValue(this.jPBarGeneral.getMaximum());

		this.jBtnRun.setText("Run");
		this.jBtnRun.setActionCommand("Run");

		enabledButton(1, false); // Reload
		enabledButton(4, true); // Close

		menu[0].setEnabled(true); // Menu principal
	}

	/**
	 * Metodo para pausar el proceso
	 */
	public void pause() {
		xfhlProcess.pause();
		enabledButton(1, false); // Reload
		String aux = "¡PAUSED!";

		this.jPBarGeneral.setString(aux);
		this.jBtnRun.setText("Continue");
		this.jBtnRun.setActionCommand("Continue");
	}

	/**
	 * Metodo para continuar el proceso
	 */
	public void contin() {
		xfhlProcess.contin();
		String aux = "¡CONTINUING!";
		this.jPBarGeneral.setString(aux);
		this.jBtnRun.setText("Pause");
		this.jBtnRun.setActionCommand("Pause");

		enabledButton(1, true); // Reload
	}

	/**
	 * @param threadActivate
	 *            the threadActivate to set
	 */
	public void setThreadActivate(int threadActivate) {
		this.threadActivate = threadActivate;
	}

	/**
	 * @return the threadActivate
	 */
	public int getThreadActivate() {
		return threadActivate;
	}

	/**
	 * 
	 * @return
	 */

	public Specification getOriginalSpec() {
		return this.original;
	}

	/**
	 * This method initializes jBtnEjecutar
	 * 
	 * @return javax.swing.JButton
	 */
	private JButton getJBtnRun() {
		if (jBtnRun == null) {
			jBtnRun = new JButton();
			jBtnRun.setBounds(new Rectangle(75, 629, 120, 21));
			jBtnRun.setActionCommand("Run");
			jBtnRun.setText("Run");
			jBtnRun.setBorder(BorderFactory.createRaisedBevelBorder());
			jBtnRun.setHorizontalAlignment(JButton.CENTER);
			jBtnRun.setFont(XConstants.font);
			jBtnRun.addActionListener(this.xfhlControllerView);
			jBtnRun.setEnabled(false);
		}
		return jBtnRun;
	}

	/**
	 * This method initializes jBtnPause
	 * 
	 * @return javax.swing.JButton
	 */
	private JButton getJBtnClose() {
		if (jBtnClose == null) {
			jBtnClose = new JButton();
			jBtnClose.setBounds(new Rectangle(674, 629, 120, 21));
			jBtnClose.setText("Close");
			jBtnClose.setActionCommand("Close");
			jBtnClose.setBorder(BorderFactory.createRaisedBevelBorder());
			jBtnClose.setHorizontalAlignment(JButton.CENTER);
			jBtnClose.setFont(XConstants.font);
			jBtnClose.addActionListener(this.xfhlControllerView);
			// jBtnClose.setEnabled(false);
		}
		return jBtnClose;
	}

	/**
	 * This method initializes jBtnStop
	 * 
	 * @return javax.swing.JButton
	 */
	private JButton getJBtnReload() {
		if (jBtnReload == null) {
			jBtnReload = new JButton();
			jBtnReload.setBounds(new Rectangle(226, 630, 120, 21));
			jBtnReload.setText("Reload");
			jBtnReload.setActionCommand("Reload");
			jBtnReload.setBorder(BorderFactory.createRaisedBevelBorder());
			jBtnReload.setHorizontalAlignment(JButton.CENTER);
			jBtnReload.setFont(XConstants.font);
			jBtnReload.addActionListener(this.xfhlControllerView);
			jBtnReload.setEnabled(false);
		}
		return jBtnReload;
	}

	/**
	 * This method initializes jBtnApply
	 * 
	 * @return javax.swing.JButton
	 */
	private JButton getJBtnApply() {
		if (jBtnApply == null) {
			jBtnApply = new JButton();
			jBtnApply.setBounds(new Rectangle(375, 630, 120, 21));
			jBtnApply.setText("Apply");
			jBtnApply.setActionCommand("Apply");
			jBtnApply.setBorder(BorderFactory.createRaisedBevelBorder());
			jBtnApply.setHorizontalAlignment(JButton.CENTER);
			jBtnApply.setFont(XConstants.font);
			jBtnApply.addActionListener(xfhlControllerView);
			jBtnApply.setEnabled(false);
		}
		return jBtnApply;
	}

	/**
	 * This method initializes jBtnSave
	 * 
	 * @return javax.swing.JButton
	 */
	private JButton getJBtnSave() {
		if (jBtnSave == null) {
			jBtnSave = new JButton();
			jBtnSave.setBounds(new Rectangle(525, 629, 120, 21));
			jBtnSave.setText("Save");
			jBtnSave.setActionCommand("Save");
			jBtnSave.setBorder(BorderFactory.createRaisedBevelBorder());
			jBtnSave.setHorizontalAlignment(JButton.CENTER);
			jBtnSave.setFont(XConstants.font);
			jBtnSave.addActionListener(this.xfhlControllerView);
			jBtnSave.setEnabled(false);
		}
		return jBtnSave;
	}

	/**
	 * Funcion que nos estima el tiempo restante
	 * 
	 * @return devuelve un string en formato HH:MM:SS con el tiempo
	 */
	public String totalTime() {
		long milisecond = System.currentTimeMillis() - this.timeIni;

		// obtenemos los segundos
		long segundos = milisecond / 1000;

		// obtenemos las horas
		long horas = segundos / 3600;

		// restamos las horas para continuar con minutos
		segundos -= horas * 3600;

		// igual que el paso anterior
		long minutos = segundos / 60;
		segundos -= minutos * 60;

		String seg = "" + segundos;
		if (seg.length() == 1)
			seg = "0" + seg;

		String min = "" + minutos;
		if (min.length() == 1)
			min = "0" + min;

		return "" + horas + ":" + min + ":" + seg;
	}

} // @jve:decl-index=0:visual-constraint="10,10"

